use futures::future::BoxFuture;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock;

#[derive(Debug, Clone)]
pub struct Context {
    pub id: u32,
    pub name: String,
    pub url: String,
    pub payload: Vec<u8>,
}

pub trait FromContext: Send + Sync {
    fn from_context(context: &Context) -> Self;
}

pub trait Handler<T>: Send + Sync {
    fn call(&self, context: Context) -> BoxFuture<'static, ()>;
}

type HandlerBox = dyn Fn(Context) -> BoxFuture<'static, ()> + Send + Sync;

pub struct HandlerManager {
    handlers: Arc<RwLock<HashMap<&'static str, Arc<HandlerBox>>>>,
}

impl HandlerManager {
    pub fn new() -> Self {
        Self {
            handlers: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub async fn register<T, H>(&self, name: &'static str, handler: H)
    where
        H: Handler<T> + 'static,
        T: Send,
    {
        let wrapper = move |ctx: Context| handler.call(ctx);

        self.handlers.write().await.insert(name, Arc::new(wrapper));
    }

    pub async fn execute(&self, name: &'static str, ctx: Context) -> Result<(), HandlerError> {
        let handler = self
            .handlers
            .read()
            .await
            .get(name)
            .cloned()
            .ok_or(HandlerError::NotFound)?;

        handler(ctx).await;
        Ok(())
    }
}

#[derive(Debug)]
pub enum HandlerError {
    NotFound,
    ExecutionFailed,
}

// 为Handler实现提供自动推导
macro_rules! impl_handler {
    ([$($ty:ident),*]) => {
        impl<F, Fut, $($ty,)*> Handler<($($ty,)*)> for F
        where
            F: Fn($($ty,)*) -> Fut + Send + Sync + Copy + 'static,
            Fut: Future<Output=()> + Send + 'static,
            $( $ty: FromContext + Send,)*
        {
            fn call(&self, context: Context) -> BoxFuture<'static, ()> {
                let fun = *self;
                Box::pin(async move {
                    fun($( $ty::from_context(&context), )*).await;
                })
            }
        }
    };
}

macro_rules! all_the_tuples {
    ($name:ident) => {
        $name!([]);
        $name!([T1]);
        $name!([T1, T2]);
        $name!([T1, T2, T3]);
        $name!([T1, T2, T3, T4]);
        $name!([T1, T2, T3, T4, T5]);
        $name!([T1, T2, T3, T4, T5, T6]);
        $name!([T1, T2, T3, T4, T5, T6, T7]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]);
    };
}

all_the_tuples!(impl_handler);

// 使用Arc优化Context访问
#[derive(Clone)]
pub struct SharedContext(Arc<Context>);

impl FromContext for SharedContext {
    fn from_context(context: &Context) -> Self {
        SharedContext(Arc::new(context.clone()))
    }
}

// 异步安全的参数提取
pub struct AsyncParams(Arc<str>);

impl FromContext for AsyncParams {
    fn from_context(context: &Context) -> Self {
        AsyncParams(context.name.as_str().into())
    }
}

pub struct AsyncId(u32);

impl FromContext for AsyncId {
    fn from_context(context: &Context) -> Self {
        AsyncId(context.id)
    }
}

async fn optimized_handler(
    AsyncParams(name): AsyncParams,
    AsyncId(id): AsyncId,
    SharedContext(ctx): SharedContext,
) {
    println!("Handling {} (id:{}) for {}", name, id, ctx.url);
}

#[tokio::test]
async fn test_optimized() {
    let manager = HandlerManager::new();
    manager.register("opt", optimized_handler).await;

    let ctx = Context {
        id: 2,
        name: "LoongWang".to_string(),
        url: "loongWang.com".to_string(),
        payload: Vec::new(),
    };
    manager.execute("opt", ctx).await.unwrap();
}
